/*
 * Copyright (c) 2010 Mathew Hall, University of Sheffield.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *
 * Neither the name of the University of Sheffield nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package translator;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Arrays;

import primitives.cluster.ClusterHead;
import primitives.cluster.ClusterNode;
import primitives.cluster.IClusterLevel;
import primitives.graph.Graph;
import primitives.graph.Node;
import primitives.graph.Transition;
import search.fitnessfunctions.util.EdgeSubsumptionTransformer;

public class DOTWriter implements GraphWriter {

    private static final String PREAMBLE = "digraph g{\ncompound = true\n";

	public static final String NOTE_START = "###4358b5009c67d0e3<";
	public static final String NOTE_END   = "###1d7fbf1663fcd3bf>";
	
	private boolean printRefCounts = false;
	
	public void setPrintRefCounts(boolean p){
		printRefCounts = p;
	}
	
	

	public String getDOT(ClusterHead ch, String notes){
		//ch = (ClusterHead)EdgeSubsumptionTransformer.transform(ch);
        StringBuilder ret = new StringBuilder(PREAMBLE);
		
		if(!"".equals(notes)){	
			ret.append("#Notes are attached. \n");
			ret.append(NOTE_START + "\n");
			notes =notes.replaceAll("(\r\n|\r|\n)+","\n#");
			ret.append(notes);
			ret.append("\n");
			ret.append(NOTE_END + "\n");
		} //*/
        // TODO: fix this to write clusters with states in and then the states,
        for (int i = 0; i < 3; i++) {
            ret .append("\n");
        }
        ret.append(getTransitions(ch.getNodes()));

        int i = 0;

        for (IClusterLevel l : ch.getChildren()) {
            ret.append(writeCluster(l, String.format("%d", i++)));
        }

		

        ret.append("\n}\n");
        return ret.toString();
	}

    public String getDOT(ClusterHead ch) {
        return getDOT(ch, "");
        
        
    }

    public String getTransitions(Collection<Node> nodes) {
        StringBuilder ret = new StringBuilder();
        for (Node n : nodes) {
            for (Transition t : n.getTransitionsAsT()) {
				
				if(!t.getSuperstate()){
					

					
                	ret .append(n.getLabel() + " -> "
                        + t.getDestinationNode().getLabel() + "[label=\""
                        + t.getLabel() + "\"]\n");
				}
            }
            ret.append("\n");
        }
        return ret.toString();
    }

	public String getSuperstateTransitions(Collection<Node> nodes, String name){
		 StringBuilder ret = new StringBuilder();
	        for (Node n : nodes) {
	            for (Transition t : n.getTransitionsAsT()) {

					if(t.getSuperstate()){
						
	                	ret .append(n.getLabel() + " -> "
	                        + t.getDestinationNode().getLabel() + "["
	                        + t.getLabel() + "ltail="+ name +"];\n");
					}
	            }
	            ret.append("\n");
	        }
	        return ret.toString();
		
	}

    public String getDOT(Graph g) {
        StringBuilder ret = new StringBuilder(PREAMBLE);
        for (Node n : g.getNodes()) {
			String x = "";
			if(printRefCounts){
				x = "[label=\"" + n.getLabel() + "~"+ n.refCount + "\"]";
			}
	
            ret.append(n.getLabel() + x+";\n");
        }

        ret.append("\n\n\n");

        ret.append(getTransitions(g.getTransitions().keySet()));
        ret.append("\n}\n");
        return ret.toString();
    }

    public String writeCluster(IClusterLevel l, String name) {
        if (!l.encapsulates()) {
            return String.format("subgraph \"cluster%s\"{\n%s\n}", name, writeCluster(l));
        }

		if(l.getNodes().size() <= 1){
			return String.format("subgraph \"cluster%s\"{\n%s\n}", name, writeCluster(l));
		}

        int c = 0;
        int num = 0;
        ClusterNode cl = (ClusterNode) l;
        String subMods = "";
		//if there is only one child of this cluster then don't print out a box around a box
		if(cl.getChildren().size() == 1){
			//Using Set there is no get so iterate over the 1 element set:
			for(IClusterLevel el: cl.getChildren()){
				return writeCluster(el,name);
			}
		}

        for (IClusterLevel el : cl.getChildren()) {

            if (!el.encapsulates()) {
                c++;
            }
        }

		

        for (IClusterLevel el : cl.getChildren()) {
            String subMod = "";
            //if (!el.encapsulates() && c == 1) {
                // don't do anything, there's more than one clusterleaf
                // so they'll be printed as normal.
                
            //} else {
	

                subMod = writeCluster(el, name + "_" + num++);
            //}

            subMods += subMod;
        }

		String trans = getSuperstateTransitions(l.getNodes(),name);

        return String.format("subgraph \"cluster%s\""
                + "{"
                + "\n%s\n"
                + "%s"
                + "\n"
 				+ "%s"
                + "}\n", name, writeCluster(l), subMods,trans);



    }

    public String writeCluster(IClusterLevel l) {
        return "\t" + join(getNodeNames(l), ";\n\t")+ "\n";
    }



	//XXX: refactor this to go somewhere else, for now let's just ruin OO principles:
    public static String join(Collection<? extends Object> c, String d) {
        StringBuilder ret = new StringBuilder();
        Iterator<? extends Object> iter = c.iterator();
        while (iter.hasNext()) {
            ret.append((iter.next()).toString());
            if (iter.hasNext()) {
                ret.append(d);
            }
        }
        return ret.toString();

    }

    public static Set<String> getNodeNames(IClusterLevel l) {
        HashSet<String> nodes = new HashSet<String>();
        for (Node n : l.getNodes()) {
            nodes.add(n.getLabel());
        }
        return nodes;
    }

    @Override
    public String getRepresentation(ClusterHead ch) {
        return getDOT(ch);
    }

    @Override
    public String getRepresentation(Graph g) {
        // TODO Auto-generated method stub
        return getDOT( g);
    }
}
